package SortAlgorithm.大文件排序;

import com.alibaba.fastjson.JSON;
import sun.reflect.misc.FieldUtil;

import java.io.*;
import java.util.*;

public class FileUnits {

    /**
     * 快速原则
     */

    //文件读取-根据偏移量读入字符数组 一个char一个中文或一个字母 1char=2byte
    public int readFile(String path,char[] buffer,int start) throws IOException {

        if(buffer==null||buffer.length==0){
            System.out.println("数组error");
            return -1;
        }

        FileReader reader=new FileReader(path);
        reader.skip(start);//跳过这么多;里面需要读许多次,较费时

        //针对数组的偏移量
        int read = reader.read(buffer);

        reader.close();

        return read;

    }

    //文件读取-根据偏移量读入字节数组 三个byte一个中文或一个字母 很奇怪哦~ 1char=2byte 但是1char就能存一个中文
    public byte[] readFile(String path,byte[] buffer,int start) throws IOException {

        if(buffer==null||buffer.length==0){
            System.out.println("数组error");
            return null;
        }

        InputStream inputStream=new FileInputStream(path);
        inputStream.skip(start);
        int count = inputStream.read(buffer);
        if(count==-1){
            return null;
        }
        if(count!=1024){
            byte[] newB=new byte[count];
            System.arraycopy(buffer,0,newB,0,count);
            return newB;
        }

        byte b=buffer[buffer.length-1];
        if(b==44){
            return buffer;
        }
        byte[] bb=new byte[10];
        int index=0;
        while (b!=44){
            //多读一点
            b = (byte) inputStream.read();
            if(b==-1){
                break;
            }
            bb[index++]=b;
        }
        byte[] newB=new byte[buffer.length+index];
        System.arraycopy(buffer,0,newB,0,buffer.length);
        System.arraycopy(bb,0,newB,buffer.length,index);
        return newB;
    }

    //文件读取-行读取
    public String readFileLine(LineNumberReader lineNumberReader) throws IOException {

        String result = lineNumberReader.readLine();

        return result;
    }

    //创建新文件
    public void createFile(String path) throws IOException {
        File file=new File(path);
        file.createNewFile();//创建新文件
    }

    //删除文件
    public void deleteFile(String path) {
        File file=new File(path);
        file.delete();
    }

    //文件写入-追加
    public void writeFile(String path,byte[] bytes,boolean isAppend) throws IOException {
        File file=new File(path);
        FileOutputStream fileOutputStream=new FileOutputStream(file,isAppend);//append设置为true
        fileOutputStream.write(bytes);
        fileOutputStream.flush();
        fileOutputStream.close();
    }

    //生成数据
    public void createData() throws IOException {
        String path="C:\\Users\\99468\\Desktop\\fileSort\\data.txt";
        Random random=new Random();
        //生成文件
        for (int i = 0; i < 100; i++) {
            StringBuilder stringBuilder=new StringBuilder();
            for (int j = 0; j < 10000; j++) {
                stringBuilder.append(random.nextInt(Integer.MAX_VALUE)).append(",");
            }
            if(i==100-1){
                //去除最后的逗号
                stringBuilder.delete(stringBuilder.length()-1,stringBuilder.length());
            }
            this.writeFile(path,stringBuilder.toString().getBytes(),true);
        }
    }

    public LineNumberReader getLineUnit(String path) throws FileNotFoundException {
        InputStream inputStream=new FileInputStream(path);
        InputStreamReader inputStreamReader=new InputStreamReader(inputStream);
        LineNumberReader lineNumberReader=new LineNumberReader(inputStreamReader);
        return lineNumberReader;
    }

    public int splitFile(String basePath) throws IOException {
        int indexCount=0;

        byte[] buffer=new byte[1024*1000];

        InputStream inputStream=new FileInputStream("C:\\Users\\99468\\Desktop\\fileSort\\data.txt");


        while (true){
            int count = inputStream.read(buffer);
            if(count==-1){
                break;//读完了
            }
            indexCount++;
            if(count!=buffer.length){
                byte[] newB=new byte[count];
                System.arraycopy(buffer,0,newB,0,count);
                //写入
                this.writeFile(basePath+(indexCount-1)+".txt",newB,false);
                continue;
            }
            byte b=buffer[buffer.length-1];
            if(b==44){
                //写入
                this.writeFile(basePath+(indexCount-1)+".txt",buffer,false);
                continue;
            }
            byte[] bb=new byte[10];
            int index=0;
            while (b!=44){
                //多读一点
                b = (byte) inputStream.read();
                if(b==-1){
                    break;
                }
                bb[index++]=b;
            }
            byte[] newB=new byte[buffer.length+index];
            System.arraycopy(buffer,0,newB,0,buffer.length);
            System.arraycopy(bb,0,newB,buffer.length,index);
            //写入
            this.writeFile(basePath+(indexCount-1)+".txt",newB,false);
        }

        return indexCount;
    }

    //字符串快速排序
    public void fastSortArr(String[] arr,Comparator<String> comparator) {

        splitArr(arr,0,arr.length-1,comparator,new Random());

    }

    //分割
    public void splitArr(String[] arr,int left,int right,Comparator<String> comparator,Random random){

        if(left>=right){
            return;
        }

        //base
        int baseIndex=random.nextInt(right-left+1)+left;
        swap(arr,left,baseIndex);
        int midIndex = sort(arr, left, right, comparator);
        splitArr(arr,left,midIndex-1,comparator,random);
        splitArr(arr,midIndex+1,right,comparator,random);
    }

    //排序
    public int sort(String[] arr,int left,int right,Comparator<String> comparator) {

        while (left<right){

            String str=arr[left];

            //从右边找一个比str小的元素
            while (left<right&& comparator.compare(str, arr[right]) <= 0){
                right--;
            }

            //交换
            swap(arr,left,right);

            //从左边找一个比arr[right]大的元素
            while (left<right&& comparator.compare(str,arr[left]) >= 0){
                left++;
            }

            swap(arr,left,right);
        }

        return left;
    }

    private void swap(String[] arr,int left,int right){
        String temp=arr[left];
        arr[left]=arr[right];
        arr[right]=temp;
    }

    public void deleteFile(){
        String basePath="C:\\Users\\99468\\Desktop\\fileSort\\data";
        for (int i = 0; i < 98; i++) {
            File file=new File(basePath +i+".txt");
            file.delete();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {

        long startTime=System.currentTimeMillis();

        String basePath="C:\\Users\\99468\\Desktop\\fileSort\\data";
        String path="C:\\Users\\99468\\Desktop\\fileSort\\data.txt";

        FileUnits fileUnits=new FileUnits();
        fileUnits.createData();

        //获取操作流
        LineNumberReader lineNumberReader = fileUnits.getLineUnit(path);
//        切割文件,返回多少个文件夹
        int count = fileUnits.splitFile(basePath);
        lineNumberReader.close();//会将上层也关闭
        System.gc();

        for (int i = 0; i < count; i++) {
            //读取各个文件,对单个文件排序
            lineNumberReader = fileUnits.getLineUnit(basePath +i+".txt");
            String itemContent = lineNumberReader.readLine();
            String[] arr = itemContent.split(",");
            fileUnits.fastSortArr(arr, Comparator.comparingInt(Integer::parseInt));
            lineNumberReader.close();
            System.gc();
            //写入单个文件
            StringBuilder stringBuilder=new StringBuilder();
            for (int i1 = 0; i1 < arr.length-1; i1++) {
                //控制字节数,每个文件的行存1024byte
                stringBuilder.append(arr[i1]).append(",");
            }
            stringBuilder.append(arr[arr.length-1]);
            fileUnits.writeFile(basePath +i+".txt",stringBuilder.toString().getBytes(),false);
            System.gc();
        }

        List<List<Integer>> lists=new ArrayList<>();
        //每个文件的跳过量+当前小部分顺序集合
        PriorityQueue<List<Integer>> priorityQueue=new PriorityQueue<>(Comparator.comparingInt(o -> o.get(2)));
        //0位是当前文件的偏移量
        for (int i = 0; i < count; i++) {
            List<Integer> list=new ArrayList<>();
            lists.add(list);
            list.add(i);//第n个文件
            byte[] buffer=new byte[1024];
            byte[] bytes = fileUnits.readFile(basePath + i + ".txt", buffer, 0);
            list.add(bytes.length);
            String content = new String(bytes);
            String[] arr = content.split(",");
            for (String s : arr) {
                list.add(Integer.parseInt(s));
            }
            priorityQueue.add(list);
        }

        String sortPath="C:\\Users\\99468\\Desktop\\fileSort\\sortDate.txt";
        int currentResultIndex=0;
        int lastIndex=0;
        int[] result=new int[1024];

        while (!priorityQueue.isEmpty()){
            List<Integer> list = priorityQueue.poll();
            Integer num = list.remove(2);
            result[currentResultIndex++]=num;
            lastIndex=currentResultIndex;
            if(currentResultIndex==1024){
                //可以将结果写入了
                StringBuilder stringBuilder=new StringBuilder();
                for (int value : result) {
                    stringBuilder.append(value).append(",");
                }
                fileUnits.writeFile(sortPath,stringBuilder.toString().getBytes(),true);
                currentResultIndex=0;
            }
            if(list.size()==2){
                //当前文件数据不足,继续录入
                int fileIndex=list.get(0);
                int skipIndex=list.get(1);
                byte[] buffer=new byte[1024];
                buffer = fileUnits.readFile(basePath + fileIndex + ".txt", buffer, skipIndex);
                if(buffer==null){
                    continue;//当前文件读完了
                }
                String[] arr = new String(buffer).split(",");
                list.set(1,list.get(1)+buffer.length);//更新行数
                for (String s : arr) {
                    list.add(Integer.parseInt(s));
                }
            }
            priorityQueue.add(list);
        }


        if(lastIndex!=1024) {
            StringBuilder stringBuilder = new StringBuilder();

            for (int i = 0; i < lastIndex-1; i++) {
                stringBuilder.append(result[i]).append(",");
            }
            stringBuilder.append(result[result.length-1]);

            fileUnits.writeFile(sortPath, stringBuilder.toString().getBytes(), true);
        }

        System.out.println("花费:"+(System.currentTimeMillis()-startTime)+"秒");
    }

}
